/************************* (C) COPYRIGHT 2011 J&DSoft **************************
* File Name          : SizeVariable.с
* Author             : Litvin Yuriy
* TS Idea            : 2011
* TS Version         : 2013-08-06 15:05:14
* Description        : Функции помещения и возвращения размеров переменной длинны 
*                      из потока данных.
*******************************************************************************/


/* Includes ------------------------------------------------------------------*/
#include "SizeVariable.h"



/* Exported functions --------------------------------------------------------*/

// Возвращает количество эффективных бит DSV из потока (пример сохраненных чисел: 0x7F = 7, 0x0111 = 9)
ui  DSV_Stream_GetCountEffectiveBits(const c8 * pStream)
{
    register ui r = 0;
    register u8 b = (*(u8 *)pStream & 0x7F);

    while (b != 0)
    {
        r++;
        b >>= 1;
    }

    while (*(const s8 *)pStream < 0)
    {
        r += 7;
        pStream++;
    }

    return r;
}


// Возвращает длину DSV в байтах из потока (пример сохраненных чисел: 0x7F = 1, 0x80 = 2, 0x0111 = 2)
ui  DSV_Stream_GetLength(const c8 * pStream)
{
    if (*(const s8 *)pStream >= 0)
    {
        return 1;
    }
    else if (*(const s8 *)(pStream + 1) >= 0)
    {
        return 2;
    }
    else if (*(const s8 *)(pStream + 2) >= 0)
    {
        return 3;
    }
    else if (*(const s8 *)(pStream + 3) >= 0)
    {
        return 4;
    }
    else
    {
        ui Result = 5;
        const s8 * pInt1 = (const s8 *)(pStream + 4);
        while (*pInt1 < 0)
        {
            Result++;
            pInt1++;
        }
        return Result;
    }
}


// Возвращает длину DSV в байтах из size_t (пример чисел: 0x7F = 1, 0x80 = 2, 0x0111 = 2)
ui  DSV_SizeT_GetLength(size_t Size)
{
    register ui r = 0;

    while (Size >>= 1)
    {
        r++;
    }

    return (r / 7) + 1;
}


// Производит проверку валидности объекта DSV в рамках заданного диапазона потока
b   DSV_Stream_ObjectInRange(const c8 * pStream, const c8 * const pStreamEnd)
{
    while (pStream < pStreamEnd && *(const s8 *)pStream < 0)
    {
        pStream++;
    }

    return (pStream < pStreamEnd);
}


// Помещает размер переменной длинны в поток, перемещает указатель потока, если удачно
b   DSV_Stream_SetSizeT(c8 ** ppStream, const c8 * const pStreamEnd, size_t Size)
{
    u8 * pStream = (u8 * )*ppStream;
    ui  Length = sizeof(size_t) - 1;
    u8  Byte;
    b   bSave = bFALSE; // Сохранять все последующие байты?

    // Размер больше максимального в size_t? (если не проверить, будут проблемы с извлечением значения из потока)
    if ((Size >> (sizeof(size_t) * 7)) > 0)
    {
        return bFALSE;
    }

    for (; Length > 0; Length--)
    {
        // 7 эффективных бит размера
        Byte = (u8)((Size >> (7 * Length)) & 0x7F);
        if (bSave || Byte != 0)
        {
            if(pStream >= (const u8 * const)pStreamEnd)
            {
                return bFALSE;
            }
            *pStream = Byte | 0x80;
            pStream++;
            bSave = bTRUE;
        }
    }
    *pStream = (u8)(Size & 0x7F);
    pStream++;

    // Смещаем указатель потока
    *ppStream = (c8 * )pStream;
    return bTRUE;
}


// Возвращает размер переменной длинны из потока, перемещает указатель потока, если удачно
b   DSV_Stream_GetSizeT(const c8 ** ppStream, const c8 * const pStreamEnd, size_t * pSize)
{
    const u8 * pStream = (const u8 *)*ppStream;
    size_t Size = 0;
    ui  Length = 0;
    u8  Byte;

    do
    {
        // Выход размера за границы size_t?
        if (++Length > sizeof(size_t) || (pStreamEnd && pStream >= (const u8 * const)pStreamEnd))
        {
            return bFALSE;
        }
        Byte = *pStream;
        pStream++;
        // 7 эффективных бит размера
        Size = (Size << 7) | (Byte & 0x7F);
    }while (Byte >= 0x80);

    *pSize = Size;
    // Смещаем указатель потока
    *ppStream = (c8 * )pStream;
    return bTRUE;
}


// Записывает значение Value по адресу pData, возвращает:
// [1;sizeof(size_t)] - ok, длина записаного в формате DSV значения,
// 0 - error, значение Value не помещается в длинну sizeof(size_t) в формате DSV
ui  DSV_pData_SetSizeT(c8 * pData, size_t Value)
{
    if (Value < 0x80u)
    {
        ((u8 *)pData)[0] = (u8)Value;
        return 1;
    }
    if (Value < 0x4000u)
    {
        ((u8 *)pData)[0] = (u8)(Value >> 7) | 0x80;
        ((u8 *)pData)[1] = (u8)(Value & 0x7F);
        return 2;
    }
    if (sizeof(size_t) >= 4)
    {
        if (Value < 0x200000u)
        {
            ((u8 *)pData)[0] = (u8)(Value >> 14) | 0x80;
            ((u8 *)pData)[1] = (u8)(Value >> 7) | 0x80;
            ((u8 *)pData)[2] = (u8)(Value & 0x7F);
            return 3;
        }
        if (Value < 0x10000000u)
        {
            ((u8 *)pData)[0] = (u8)(Value >> 21) | 0x80;
            ((u8 *)pData)[1] = (u8)(Value >> 14) | 0x80;
            ((u8 *)pData)[2] = (u8)(Value >> 7) | 0x80;
            ((u8 *)pData)[3] = (u8)(Value & 0x7F);
            return 4;
        }
        if (sizeof(size_t) >= 8 && Value < 0x100000000000000ull)
        {
            ui Result = DSV_pData_SetSizeT(pData, (size_t)(Value >> 28));
            if (Result != 0)
            {
                pData[Result-1] |= 0x80;
                pData += Result;
                ((u8 *)pData)[0] = (u8)(Value >> 21) | 0x80;
                ((u8 *)pData)[1] = (u8)(Value >> 14) | 0x80;
                ((u8 *)pData)[2] = (u8)(Value >> 7) | 0x80;
                ((u8 *)pData)[3] = (u8)(Value & 0x7F);
                return Result + 4;
            }
        }
    }

    return 0;
}


// Считывает значение Value по адресу pData, возвращает:
// [1;sizeof(size_t)] - ok, длина считанного в формате DSV значения,
// 0 - error, значение Value не помещается в длинну sizeof(size_t) в формате DSV
ui  DSV_pData_GetSizeT(const c8 * pData, size_t * pValue)
{
    if (((s8 *)pData)[0] >= 0)
    {
        *pValue = ((u8 *)pData)[0];
        return 1;
    }
    if (((s8 *)pData)[1] >= 0)
    {
        *pValue = ((((u8 *)pData)[0] & 0x7F)) << 7 | ((u8 *)pData)[1];
        return 2;
    }
    if (sizeof(size_t) >= 4)
    {
        if (((s8 *)pData)[2] >= 0)
        {
            *pValue = ((((u8 *)pData)[0] & 0x7F)) << 14 | ((((u8 *)pData)[1] & 0x7F)) << 7 | ((u8 *)pData)[2];
            return 3;
        }
        if (((s8 *)pData)[3] >= 0)
        {
            *pValue = ((((u8 *)pData)[0] & 0x7F)) << 21 |  ((((u8 *)pData)[1] & 0x7F)) << 14 | ((((u8 *)pData)[2] & 0x7F)) << 7 | ((u8 *)pData)[3];
            return 4;
        }
        if (sizeof(size_t) >= 8)
        {
            size_t U28Lo, U28Hi = ((((u8 *)pData)[0] & 0x7F)) << 21 |  ((((u8 *)pData)[1] & 0x7F)) << 14 | ((((u8 *)pData)[2] & 0x7F)) << 7 | (((u8 *)pData)[3] & 0x7F);
            ui Result = DSV_pData_GetSizeT(&pData[4], &U28Lo);
            if (Result != 0)
            {
                *pValue = (U28Hi << (7 * Result)) | U28Lo;
                return 4 + Result;
            }
        }
    }

    return 0;
}


/************************* (C) COPYRIGHT 2011 J&DSoft *************************/
